import { any } from "../../core/logic";
import { guid } from "../../core/utils";
import { Channel } from "./Channel";
import { EventType, Event } from "../../event/index";
import { Component, ComponentType } from "../Component";
import { Engine } from "../../Engine";
/**
 * 动画类，其构成：
 * 1. 一个Animation由1-n个Channel构成;
 * 2. 一个Chinnel由1-n个Sampler构成。
 * @class
 * @memberof w.animation
 * @extends Clock
 * @example
 *  let sampler=new w.animation.Sampler([0,40], [[0,0],[-100,0]],"LINEAR", "none");
 *  let channel=new w.animation.Channel(m,"map_uvOffset");
 *  channel.add(sampler);
 *  let animation=new w.animation.Animation({id:"uvOffset",running:true});
 *  animation.add(channel);
 *  aniSys.add(animation);
 */
class Animation extends Component {
  /**
   * 创建一个动画实例。
   * @constructor
   * @param {String} opts.id 【可选】动画的id，缺省值:自动生成guid。
   * @param {Number} opts.loop 【可选】动画循环次数，缺省值:1。如需不限次数重复执行，可以将该参数值设置为：0。
   * @param {Boolean} opts.keep 【可选】动画执行结束是否保留,缺省值：true。如果保留，那么通过动画的start还可以再次启动，否则在执行结束后自动销毁。
   * @param {Function} opts.frameCallback【可选】动画帧的回调。
   * @param {Function} opts.keyFrameCallback【可选】动画关键帧的回调。
   * @param {Function} opts.finalFrameCallback【可选】动画结束帧的回调。
   * @param {Array} opts.keyFrames 【可选】关键帧时间。
   */
  constructor(opts = {}) {
    opts.type = ComponentType.Animation;
    super(opts);
    this.id = any(opts.id, guid());
    this.loop = any(opts.loop, 1);
    this.keep = any(opts.keep, true);
    this.channels = [];
    this.autoStart = any(opts.autoStart, true);
    this.startTime = 0;
    this.oldTime = 0;
    this.elapsedTime = 0;
    this.forward = 1; //动画播放方向1正向-1反向
    this.running = false;
    this.frameCallback = opts.frameCallback;
    this.keyFrameCallback = opts.keyFrameCallback;
    this.finalFrameCallback = opts.finalFrameCallback;
    this.skin = opts.skin;
    this._timeScale = 1;
    this._valueScale = 1;
    this.autoUpdate = true;
    this.setState(true);
  }
  get timeScale() {
    return this._timeScale;
  }
  set timeScale(v) {
    let ratio = v / this.timeScale;
    this._timeScale = v;
    this.elapsedTime*=ratio;
    for (let i = 0; i < this.channels.length; i++) {
      let channel = this.channels[i];
      for (let j = 0; j < channel.samplers.length; j++) {
        let simpler = channel.samplers[j];
        simpler.scaleTime(ratio);
      }
    }
  }
  get valueScale() {
    return this._valueScale;
  }
  set valueScale(v) {
    let ratio = v / this.valueScale;
    this._valueScale = v;
    for (let i = 0; i < this.channels.length; i++) {
      let channel = this.channels[i];
      for (let j = 0; j < channel.samplers.length; j++) {
        let simpler = channel.samplers[j];
        simpler.scaleValue(ratio);
      }
    }
  }
  get dual() {
    return this.timeLine()[1];
  }
  /**
   * 开始动画。
   * @method start
   */
  start(time) {
    this.forward = 1;
    this.startTime = now();
    this.oldTime = this.startTime;
    if (time != undefined) {
      this.elapsedTime = time; //指定时间开始
    } else {
      if (this.elapsedTime >= this.loop * this.dual) {
        this.elapsedTime = 0;
      }
    }
    this.running = true;
    this.play();
  }
  back(time) {
    this.forward = -1;
    this.startTime = now();
    this.oldTime = this.startTime;
    if (time != undefined) {
      this.elapsedTime = time;
    } else {
      if (this.elapsedTime <= 0) {
        this.elapsedTime = this.dual * this.loop;
      }
    }
    this.running = true;
    this.play();
  }

  /**
   * 暂停动画
   * @method stop
   */
  stop(time) {
    if (time != undefined) {
      this.elapsedTime = time;
      this.startTime = now();
      this.oldTime = this.startTime;
      this.running = true;
      this.play();
    }
    this.running = false;
    this.autoStart = false;
  }
  /**
   * 获取动画流逝的时间，单位:秒(s)。
   * @method getElapsedTime
   * @returns {Number}
   */
  getElapsedTime() {
    this.getDelta();
    return this.elapsedTime; //经过了多少时间
  }
  getDelta() {
    let diff = 0;
    if (this.autoStart && !this.running) {
      this.start(); //获取开始时间
      return 0;
    }
    if (this.running) {
      const newTime = now();
      diff = (newTime - this.oldTime) / 1000;
      this.oldTime = newTime;
      this.elapsedTime += diff * this.forward;
    }
    return diff;
  }
  /**
   * @property {String} id 动画的唯一标识。
   */
  set id(v) {
    this._id = v;
    return this;
  }
  get id() {
    return this._id;
  }
  /**
   * @property {Number} loop 动画循环次数。
   */
  set loop(v) {
    this._loop = v;
    return this;
  }
  get loop() {
    return this._loop;
  }
  /**
   * @property {Number} loop 动画循环次数。
   */
  set autoStart(v) {
    this._autoStart = v;
    return this;
  }
  get autoStart() {
    return this._autoStart;
  }
  /**
   * @property {Boolean} keep 动画执行完成是否保留。
   */
  set keep(v) {
    this._keep = v;
    return this;
  }
  get keep() {
    return this._keep;
  }

  /**
   * 添加动画通道。
   * @method
   * @param {w.animation.Channel} channel
   * @return {w.animation.Animation} 返回添加Channel后的Animation对象。
   */
  add(channel) {
    let c = this.get(channel.id);
    if (c) {
      this.remove(c);
    }
    this.channels.push(channel);
    return this;
  }
  /**
   * 移除指定动画通道。
   * @method
   * @param {w.animation.Channel} channel
   * @return {w.animation.Animation} 返回添加Channel后的Animation对象。
   */
  remove(channel) {
    let id = "";
    if (channel instanceof Channel) {
      id = channel.id;
    } else {
      id = channel;
    }
    for (let i = this.channels.length - 1; i >= 0; i--) {
      if (this.channels[i].id === id) {
        this.channels.splice(i, 1);
        break;
      }
    }
    return this;
  }
  get(id) {
    let channel;
    for (let i = 0; i < this.channels.length; i++) {
      if (this.channels[i] == id) {
        channel = this.channels[i];
        break;
      }
    }
    return channel;
  }
  /**
   * 移除所有动画通道。
   * @method
   * @return {Animation} 返回添加Channel后的Animation对象。
   */
  clear() {
    this.channels = [];
    return this;
  }
  play() {
    if (!this.running && !this.autoStart) {
      return;
    }
    let playTime = this.getPlayTime();
    this.getKeyFrames(playTime);

    let animationInfos = [];
    for (let i = 0; i < this.channels.length; i++) {
      let channelInfos = this.channels[i].play(playTime);
      animationInfos.push(channelInfos);
    }
    if (this.frameCallback) {
      this.frameCallback(animationInfos,playTime);
    }
    if (this.skin) {
      this.skin.update();
    }
    return animationInfos;
  }
  getKeyFrames(time) {
    if (!this.keyFrameCallback) {
      return;
    }
    let ktime = 0;
    const keytimes = this.getkeyTimes();
    for (let i = 1; i < keytimes.length; i++) {
      if (time >= keytimes[i - 1] && time < keytimes[i]) {
        ktime = keytimes[i - 1];
      }
    }
    if (time >= keytimes[keytimes.length - 1]) {
      ktime = keytimes[keytimes.length - 1];
    }
    //匹配关键点
    if (this.keytime != ktime) {
      this.keyFrameCallback(ktime);
      this.keytime = ktime;
    }
    return this;
  }
  getPlayTime() {
    let playTime = 0;
    playTime = this.getElapsedTime();
    if (this.loop === 0) {
      playTime %= this.dual;
    } else {
      if (playTime > this.dual * this.loop) {
        this.elapsedTime = playTime = this.dual * this.loop;
        this.running = false;
        this.autoStart = false;
        let event = new Event(EventType.AnimationCompleted);
        event.target = this;
        this.publish(event);
        if (!this.keep) {
          Engine.instance.register
            .manager(ComponentType.Animation)
            .remove(this.id);
        }
        if (this.finalFrameCallback) {
          this.finalFrameCallback();
        }
      } else if (playTime < 0) {
        this.elapsedTime = playTime = 0;
        this.running = false;
        this.autoStart = false;
        let event = new Event(EventType.AnimationCompleted);
        event.target = this;
        this.publish(event);
        if (!this.keep) {
          Engine.instance.register
            .manager(ComponentType.Animation)
            .remove(this.id);
        }
        if (this.finalFrameCallback) {
          this.finalFrameCallback();
        }
      } else {
        playTime %= this.dual;
      }
    }

    return playTime;
  }
  timeLine() {
    let min = Number.MAX_VALUE,
      max = Number.MIN_VALUE;
    for (let i = 0; i < this.channels.length; i++) {
      const channels = this.channels[i];
      let timeLine = channels.timeLine();
      min = Math.min(min, timeLine[0]);
      max = Math.max(max, timeLine[1]);
    }
    return [min, max];
  }
  getkeyTimes() {
    let times = new Set();
    for (let i = 0; i < this.channels.length; i++) {
      const channel = this.channels[i];
      for (let j = 0; j < channel.samplers.length; j++) {
        let sample = channel.samplers[j];
        for (let k = 0; k < sample.times.length; k++) {
          times.add(sample.times[k]);
        }
      }
    }
    times = Array.from(times);
    times.sort((a, b) => a - b);
    return times;
  }
}
function now() {
  return (typeof performance === "undefined" ? Date : performance).now(); // see #10732
}
export { Animation };
